import type { DayOfWeekEnum } from '@/types/schedule/enums';

/**
 * 日期处理工具类
 * @description 提供日期计算、格式化、验证等功能
 */
export class DateUtils {
  /**
   * 日期格式常量
   */
  static readonly DATE_FORMAT = 'YYYY-MM-DD';
  static readonly DATETIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
  static readonly TIME_FORMAT = 'HH:mm';
  static readonly DISPLAY_DATE_FORMAT = 'YYYY年MM月DD日';
  static readonly DISPLAY_DATETIME_FORMAT = 'YYYY年MM月DD日 HH:mm';

  /**
   * 获取当前日期字符串
   * @returns 当前日期 (YYYY-MM-DD)
   */
  static getCurrentDate(): string {
    return new Date().toISOString().split('T')[0];
  }

  /**
   * 获取当前时间字符串
   * @returns 当前时间 (HH:mm)
   */
  static getCurrentTime(): string {
    const now = new Date();
    return `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
  }

  /**
   * 获取当前日期时间字符串
   * @returns 当前日期时间 (YYYY-MM-DD HH:mm:ss)
   */
  static getCurrentDateTime(): string {
    return new Date().toISOString().replace('T', ' ').split('.')[0];
  }

  /**
   * 格式化日期为显示格式
   * @param date 日期字符串或Date对象
   * @returns 格式化后的日期字符串
   */
  static formatDateForDisplay(date: string | Date): string {
    const dateObj = typeof date === 'string' ? new Date(date) : date;
    const year = dateObj.getFullYear();
    const month = (dateObj.getMonth() + 1).toString().padStart(2, '0');
    const day = dateObj.getDate().toString().padStart(2, '0');
    return `${year}年${month}月${day}日`;
  }

  /**
   * 格式化日期时间为显示格式
   * @param datetime 日期时间字符串或Date对象
   * @returns 格式化后的日期时间字符串
   */
  static formatDateTimeForDisplay(datetime: string | Date): string {
    const dateObj = typeof datetime === 'string' ? new Date(datetime) : datetime;
    const datePart = this.formatDateForDisplay(dateObj);
    const hours = dateObj.getHours().toString().padStart(2, '0');
    const minutes = dateObj.getMinutes().toString().padStart(2, '0');
    return `${datePart} ${hours}:${minutes}`;
  }

  /**
   * 验证日期格式是否正确
   * @param dateString 日期字符串
   * @returns 是否为有效日期
   */
  static isValidDate(dateString: string): boolean {
    if (!dateString || typeof dateString !== 'string') {
      return false;
    }

    // 检查格式是否为 YYYY-MM-DD
    const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
    if (!dateRegex.test(dateString)) {
      return false;
    }

    const date = new Date(dateString);
    return date instanceof Date && !isNaN(date.getTime()) && date.toISOString().split('T')[0] === dateString;
  }

  /**
   * 验证时间格式是否正确
   * @param timeString 时间字符串
   * @returns 是否为有效时间
   */
  static isValidTime(timeString: string): boolean {
    if (!timeString || typeof timeString !== 'string') {
      return false;
    }

    // 检查格式是否为 HH:mm
    const timeRegex = /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/;
    return timeRegex.test(timeString);
  }

  /**
   * 获取日期对应的星期几
   * @param date 日期字符串或Date对象
   * @returns 星期几 (1-7, 1为星期一)
   */
  static getDayOfWeek(date: string | Date): DayOfWeekEnum {
    const dateObj = typeof date === 'string' ? new Date(date) : date;
    const day = dateObj.getDay();
    // JavaScript的getDay()返回0-6，0为星期日，需要转换为1-7，1为星期一
    return (day === 0 ? 7 : day) as DayOfWeekEnum;
  }

  /**
   * 根据开始日期和星期几计算下一个指定星期的日期
   * @param startDate 开始日期
   * @param targetDayOfWeek 目标星期几
   * @returns 下一个指定星期的日期
   */
  static getNextDateByDayOfWeek(startDate: string | Date, targetDayOfWeek: DayOfWeekEnum): Date {
    const startDateObj = typeof startDate === 'string' ? new Date(startDate) : new Date(startDate);
    const startDayOfWeek = this.getDayOfWeek(startDateObj);
    
    let daysToAdd = targetDayOfWeek - startDayOfWeek;
    if (daysToAdd <= 0) {
      daysToAdd += 7; // 如果目标星期已过或是今天，则计算下周的该星期
    }
    
    const nextDate = new Date(startDateObj);
    nextDate.setDate(startDateObj.getDate() + daysToAdd);
    return nextDate;
  }

  /**
   * 计算课程实例的具体日期
   * @param startDate 开始日期
   * @param targetDayOfWeek 目标星期几
   * @param weekOffset 周偏移量（第几周）
   * @returns 课程实例日期
   */
  static calculateInstanceDate(startDate: string | Date, targetDayOfWeek: DayOfWeekEnum, weekOffset: number): Date {
    const startDateObj = typeof startDate === 'string' ? new Date(startDate) : new Date(startDate);
    const startDayOfWeek = this.getDayOfWeek(startDateObj);
    
    // 计算到目标星期几需要的天数
    let daysToTarget = targetDayOfWeek - startDayOfWeek;
    if (daysToTarget < 0) {
      daysToTarget += 7;
    }
    
    // 加上周偏移量
    const totalDays = daysToTarget + (weekOffset * 7);
    
    const instanceDate = new Date(startDateObj);
    instanceDate.setDate(startDateObj.getDate() + totalDays);
    return instanceDate;
  }

  /**
   * 生成课程实例日期列表
   * @param startDate 开始日期
   * @param dayOfWeek 星期几
   * @param totalCount 总次数
   * @returns 日期列表
   */
  static generateInstanceDates(startDate: string | Date, dayOfWeek: DayOfWeekEnum, totalCount: number): string[] {
    const dates: string[] = [];
    
    for (let i = 0; i < totalCount; i++) {
      const instanceDate = this.calculateInstanceDate(startDate, dayOfWeek, i);
      dates.push(instanceDate.toISOString().split('T')[0]);
    }
    
    return dates;
  }

  /**
   * 计算两个日期之间的天数差
   * @param startDate 开始日期
   * @param endDate 结束日期
   * @returns 天数差
   */
  static getDaysDifference(startDate: string | Date, endDate: string | Date): number {
    const start = typeof startDate === 'string' ? new Date(startDate) : startDate;
    const end = typeof endDate === 'string' ? new Date(endDate) : endDate;
    
    const timeDiff = end.getTime() - start.getTime();
    return Math.ceil(timeDiff / (1000 * 3600 * 24));
  }

  /**
   * 计算两个日期之间的周数差
   * @param startDate 开始日期
   * @param endDate 结束日期
   * @returns 周数差
   */
  static getWeeksDifference(startDate: string | Date, endDate: string | Date): number {
    const daysDiff = this.getDaysDifference(startDate, endDate);
    return Math.ceil(daysDiff / 7);
  }

  /**
   * 获取指定日期所在周的开始日期（星期一）
   * @param date 指定日期
   * @returns 周开始日期
   */
  static getWeekStartDate(date: string | Date): Date {
    const dateObj = typeof date === 'string' ? new Date(date) : new Date(date);
    const dayOfWeek = this.getDayOfWeek(dateObj);
    const daysToSubtract = dayOfWeek - 1; // 星期一为1，所以减去(dayOfWeek - 1)天
    
    const weekStart = new Date(dateObj);
    weekStart.setDate(dateObj.getDate() - daysToSubtract);
    return weekStart;
  }

  /**
   * 获取指定日期所在周的结束日期（星期日）
   * @param date 指定日期
   * @returns 周结束日期
   */
  static getWeekEndDate(date: string | Date): Date {
    const weekStart = this.getWeekStartDate(date);
    const weekEnd = new Date(weekStart);
    weekEnd.setDate(weekStart.getDate() + 6);
    return weekEnd;
  }

  /**
   * 获取指定日期所在月的开始日期
   * @param date 指定日期
   * @returns 月开始日期
   */
  static getMonthStartDate(date: string | Date): Date {
    const dateObj = typeof date === 'string' ? new Date(date) : new Date(date);
    return new Date(dateObj.getFullYear(), dateObj.getMonth(), 1);
  }

  /**
   * 获取指定日期所在月的结束日期
   * @param date 指定日期
   * @returns 月结束日期
   */
  static getMonthEndDate(date: string | Date): Date {
    const dateObj = typeof date === 'string' ? new Date(date) : new Date(date);
    return new Date(dateObj.getFullYear(), dateObj.getMonth() + 1, 0);
  }

  /**
   * 获取指定日期范围内的所有日期
   * @param startDate 开始日期
   * @param endDate 结束日期
   * @returns 日期列表
   */
  static getDateRange(startDate: string | Date, endDate: string | Date): string[] {
    const start = typeof startDate === 'string' ? new Date(startDate) : new Date(startDate);
    const end = typeof endDate === 'string' ? new Date(endDate) : new Date(endDate);
    const dates: string[] = [];
    
    const currentDate = new Date(start);
    while (currentDate <= end) {
      dates.push(currentDate.toISOString().split('T')[0]);
      currentDate.setDate(currentDate.getDate() + 1);
    }
    
    return dates;
  }

  /**
   * 检查日期是否在指定范围内
   * @param date 要检查的日期
   * @param startDate 范围开始日期
   * @param endDate 范围结束日期
   * @returns 是否在范围内
   */
  static isDateInRange(date: string | Date, startDate: string | Date, endDate: string | Date): boolean {
    const checkDate = typeof date === 'string' ? new Date(date) : date;
    const start = typeof startDate === 'string' ? new Date(startDate) : startDate;
    const end = typeof endDate === 'string' ? new Date(endDate) : endDate;
    
    return checkDate >= start && checkDate <= end;
  }

  /**
   * 检查是否为今天
   * @param date 要检查的日期
   * @returns 是否为今天
   */
  static isToday(date: string | Date): boolean {
    const checkDate = typeof date === 'string' ? date : date.toISOString().split('T')[0];
    const today = this.getCurrentDate();
    return checkDate === today;
  }

  /**
   * 检查是否为过去的日期
   * @param date 要检查的日期
   * @returns 是否为过去的日期
   */
  static isPastDate(date: string | Date): boolean {
    const checkDate = typeof date === 'string' ? new Date(date) : date;
    const today = new Date(this.getCurrentDate());
    return checkDate < today;
  }

  /**
   * 检查是否为未来的日期
   * @param date 要检查的日期
   * @returns 是否为未来的日期
   */
  static isFutureDate(date: string | Date): boolean {
    const checkDate = typeof date === 'string' ? new Date(date) : date;
    const today = new Date(this.getCurrentDate());
    return checkDate > today;
  }

  /**
   * 添加天数到指定日期
   * @param date 基准日期
   * @param days 要添加的天数
   * @returns 新日期
   */
  static addDays(date: string | Date, days: number): Date {
    const dateObj = typeof date === 'string' ? new Date(date) : new Date(date);
    const newDate = new Date(dateObj);
    newDate.setDate(dateObj.getDate() + days);
    return newDate;
  }

  /**
   * 添加周数到指定日期
   * @param date 基准日期
   * @param weeks 要添加的周数
   * @returns 新日期
   */
  static addWeeks(date: string | Date, weeks: number): Date {
    return this.addDays(date, weeks * 7);
  }

  /**
   * 添加月数到指定日期
   * @param date 基准日期
   * @param months 要添加的月数
   * @returns 新日期
   */
  static addMonths(date: string | Date, months: number): Date {
    const dateObj = typeof date === 'string' ? new Date(date) : new Date(date);
    const newDate = new Date(dateObj);
    newDate.setMonth(dateObj.getMonth() + months);
    return newDate;
  }

  /**
   * 获取相对日期描述
   * @param date 目标日期
   * @returns 相对日期描述
   */
  static getRelativeDateDescription(date: string | Date): string {
    const targetDate = typeof date === 'string' ? new Date(date) : date;
    const today = new Date(this.getCurrentDate());
    const daysDiff = this.getDaysDifference(today, targetDate);

    if (daysDiff === 0) {
      return '今天';
    } else if (daysDiff === 1) {
      return '明天';
    } else if (daysDiff === -1) {
      return '昨天';
    } else if (daysDiff > 1 && daysDiff <= 7) {
      return `${daysDiff}天后`;
    } else if (daysDiff < -1 && daysDiff >= -7) {
      return `${Math.abs(daysDiff)}天前`;
    } else if (daysDiff > 7) {
      const weeksDiff = Math.floor(daysDiff / 7);
      return `${weeksDiff}周后`;
    } else {
      const weeksDiff = Math.floor(Math.abs(daysDiff) / 7);
      return `${weeksDiff}周前`;
    }
  }
}

/**
 * 导出便捷函数
 */

/**
 * 获取当前日期
 */
export const getCurrentDate = () => DateUtils.getCurrentDate();

/**
 * 获取当前时间
 */
export const getCurrentTime = () => DateUtils.getCurrentTime();

/**
 * 格式化日期为显示格式
 */
export const formatDateForDisplay = (date: string | Date) => DateUtils.formatDateForDisplay(date);

/**
 * 验证日期格式
 */
export const isValidDate = (dateString: string) => DateUtils.isValidDate(dateString);

/**
 * 验证时间格式
 */
export const isValidTime = (timeString: string) => DateUtils.isValidTime(timeString);

/**
 * 获取星期几
 */
export const getDayOfWeek = (date: string | Date) => DateUtils.getDayOfWeek(date);

/**
 * 计算课程实例日期
 */
export const calculateInstanceDate = (startDate: string | Date, dayOfWeek: DayOfWeekEnum, weekOffset: number) => 
  DateUtils.calculateInstanceDate(startDate, dayOfWeek, weekOffset);

/**
 * 生成课程实例日期列表
 */
export const generateInstanceDates = (startDate: string | Date, dayOfWeek: DayOfWeekEnum, totalCount: number) => 
  DateUtils.generateInstanceDates(startDate, dayOfWeek, totalCount);

/**
 * 检查是否为今天
 */
export const isToday = (date: string | Date) => DateUtils.isToday(date);

/**
 * 检查是否为过去日期
 */
export const isPastDate = (date: string | Date) => DateUtils.isPastDate(date);

/**
 * 获取相对日期描述
 */
export const getRelativeDateDescription = (date: string | Date) => DateUtils.getRelativeDateDescription(date);